﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;

namespace LifeTree.Core.Utils
{
    /// <summary>
    /// This utility class can be used to easily check parameter input and throw exceptions if they do not meet the set
    /// expectations.
    /// </summary>
    public class Guard
    {
        /// <summary>
        /// Private constructor to prevent creating an instance of this class. 
        /// </summary>
        private Guard()
        {
        }

        /// <summary>
        /// Throws an <see cref="ArgumentOutOfRangeException"/> if the given parameter is a negative number.
        /// </summary>
        /// <typeparam name="T">The type of value to check. Should implement <see cref="IComparable{T}"/> interface.</typeparam>
        /// <param name="value">The value to check.</param>
        /// <param name="message">Exception message to give when value is invalid.</param>
        /// <param name="parameterName">Name of the parameter.</param>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     The value is less than zero.
        /// </exception>
        public static void ThrowIfNegative<T>(T value, string message, string parameterName)
            where T : IComparable<T>
        {
            ThrowIfLessThan(value, default(T), message, parameterName);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentOutOfRangeException"/> if the given parameter is a number below the given minimun value.
        /// </summary>
        /// <typeparam name="T">The type of value to check. Should implement <see cref="IComparable{T}"/> interface.</typeparam>
        /// <param name="value">The value to check.</param>
        /// <param name="minValue">The minimum value allowed.</param>
        /// <param name="message">Exception message to give when value is invalid.</param>
        /// <param name="parameterName">Name of the parameter.</param>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     The value is less than zero.
        /// </exception>
        public static void ThrowIfLessThan<T>(T value, T minValue, string message, string parameterName)
            where T : IComparable<T>
        {
            if (value.CompareTo(minValue) < 0)
                throw new ArgumentOutOfRangeException(parameterName, message);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentNullException"/> exception if the passed object equals null.
        /// </summary>
        /// <param name="obj">The object to check.</param>
        /// <exception cref="ArgumentNullException">
        ///     The supplied object <paramref name="obj"/> equals to <see langword="null"/>.
        /// </exception>
        public static void ThrowIfNull(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException(string.Empty);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentNullException"/> exception if the passed object equals null.
        /// </summary>
        /// <param name="obj">>The object to check.</param>
        /// <param name="parameterName">Name of the parameter. Will be displayed with the exception details.</param>
        /// <exception cref="ArgumentNullException">
        ///     The supplied object <paramref name="obj"/> equals to <see langword="null"/>.
        /// </exception>
        public static void ThrowIfNull(object obj, string parameterName)
        {
            if (obj == null)
                throw new ArgumentNullException(parameterName);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentNullException"/> exception if the passed object equals null.
        /// </summary>
        /// <param name="obj">>The object to check.</param>
        /// <param name="parameterName">Name of the parameter. Will be displayed with the exception details.</param>
        /// <param name="message">Exception message to give when value is invalid.</param>
        /// <exception cref="ArgumentNullException">
        ///     The supplied object <paramref name="obj"/> equals to <see langword="null"/>.
        /// </exception>
        public static void ThrowIfNull(object obj, string parameterName, string message)
        {
            if (obj == null)
                throw new ArgumentNullException(parameterName, message);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentException"/> if the passed string is an emtpy string.
        /// </summary>
        /// <param name="str">The string to check.</param>
        /// <exception cref="ArgumentException">
        ///     The <paramref name="str"/> is <see langword="null"/> or empty.
        /// </exception>
        public static void ThrowIfEmptyString(string str)
        {
            if (string.IsNullOrEmpty(str))
                throw new ArgumentException(string.Empty);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentException"/> if the passed string is an emtpy string.
        /// </summary>
        /// <param name="str">The string to check.</param>
        /// <param name="message">Exception message to give when value is invalid.</param>
        /// <exception cref="ArgumentException">
        ///     The <paramref name="str"/> is <see langword="null"/> or empty.
        /// </exception>
        public static void ThrowIfEmptyString(string str, string message)
        {
            if (string.IsNullOrEmpty(str))
                throw new ArgumentException(message);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentException"/> if the passed GUID is emtpy.
        /// </summary>
        /// <param name="guid">The GUID to check.</param>
        /// <param name="message">Exception message to give when value is invalid.</param>
        /// <exception cref="ArgumentException">
        /// The <paramref name="guid"/> is <see langword="null"/> or empty.
        /// </exception>
        public static void ThrowIfEmptyGuid(Guid guid, string message)
        {
            if (guid == Guid.Empty)
                throw new ArgumentException(message);
        }

        public static void ThrowIfEmptyGuid(Guid guid)
        {
            if (guid == Guid.Empty)
                throw new ArgumentException("Argument can't be an empty Guid.");
        }

        /// <summary>
        /// Throws an <see cref="ArgumentException"/> if two values are not equal.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value1">The first value to compare.</param>
        /// <param name="value2">The second value to compare.</param>
        /// <param name="message">Exception message to give when values are not equal.</param>
        /// <exception cref="ArgumentException">
        /// Values are not equal.
        /// </exception>
        public static void ThrowIfNotEqual<T>(T value1, T value2, string message)
        {
            if (!EqualityComparer<T>.Default.Equals(value1, value2))
                throw new ArgumentException(message);
        }

        /// <summary>
        ///  Throws an <see cref="ArgumentException"/> if the passed string is an emtpy string.
        /// </summary>
        /// <param name="str">The string to check.</param>
        /// <param name="message">Exception message to give when value is invalid.</param>
        /// <param name="parameterName">Name of the parameter. Will be displayed with the exception details.</param>
        /// <exception cref="ArgumentException">
        ///     The <paramref name="str"/> is <see langword="null"/> or empty.
        /// </exception>
        public static void ThrowIfEmptyString(string str, string message, string parameterName)
        {
            if (string.IsNullOrEmpty(str))
                throw new ArgumentException(message, parameterName);
        }

        /// <summary>
        /// Thows an <see cref="ArgumentNullException"/> if the passed collection is emtpy or a <see cref="ArgumentException"/>
        /// if the collection does not contain any elements.
        /// </summary>
        /// <typeparam name="T">Types of objects.</typeparam>
        /// <param name="collection">Collection to check.</param>
        /// <param name="parameterName">Name of the parameter. Will be displayed with the exception details.</param>
        /// <param name="message">Exception message to give when value is invalid.</param>
        /// <exception cref="ArgumentNullException">
        ///     The supplied collection is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///     The supplied collection has no items.
        /// </exception>
        public static void ThrowIfEmpty<T>(IEnumerable<T> collection, string parameterName, string message)
        {
            if (collection == null)
                throw new ArgumentNullException(parameterName, message);

            if (!collection.Any())
                throw new ArgumentException(message, parameterName);
        }

        /// <summary>
        /// Throws an <see cref="ArgumentException"/> if the supplied string has a length longer than the specified length.
        /// </summary>
        /// <param name="str">String to check.</param>
        /// <param name="maxLength">The maximum string length allowed.</param>
        /// <param name="parameterName">Name of the parameter. Will be displayed with the exception details.</param>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     The supplied string is longer than the given <paramref name="maxLength"/>.
        /// </exception>
        public static void ThrowIfLonger(string str, int maxLength, string parameterName)
        {
            if (str.Length > maxLength)
                throw new ArgumentOutOfRangeException(parameterName, String.Format(CultureInfo.CurrentCulture, "{0} cannot be longer than {1} characters.", parameterName, maxLength));
        }

        /// <summary>
        /// Throws an <see cref="ArgumentException"/> if input string does not match regex pattern.
        /// </summary>
        /// <param name="str">The input string.</param>
        /// <param name="regexPattern">The regex pattern.</param>
        /// <param name="parameterName">Name of the parameter being validated.</param>
        /// <exception cref="ArgumentException">
        ///     The <paramref name="str"/> does not match the <paramref name="regexPattern"/>.
        /// </exception>
        public static void ThrowIfDoNotMatch(string str, string regexPattern, string parameterName)
        {
            if (!Regex.IsMatch(str, regexPattern))
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "String '{0}' does not match the regex pattern '{1}'.", str, regexPattern), parameterName);
        }
    }
}
